using Moq;
using NUnit.Framework;
using Tycho.Parser.Peg;

namespace Tycho.Parser.Tests.Peg {
    [TestFixture]
    public class ProductionMemoTableSpecification {
        [Test]
        public void ShouldMemoriseParseResult () {
            var memo = new ProductionMemoTable ();

            var prod = new Mock<IProduction> ().Object;

            IProductionMemoKey key = memo.CreateKey (prod, 4);

            ParseResult result;
            Assert.False(memo.TryGetResult (key, out result));

            var storedResult = new ParseResult (0, null, null);
            memo.SetResult (key, storedResult);

            Assert.True(memo.TryGetResult (key, out result));

            Assert.That (result, Is.SameAs (storedResult));
        }

        [Test]
        public void ShouldHaveDifferentEntriesForDifferentProductions () {
            var memo = new ProductionMemoTable ();

            IProductionMemoKey key1 = memo.CreateKey (new Mock<IProduction> ().Object, 1);
            IProductionMemoKey key2 = memo.CreateKey (new Mock<IProduction> ().Object, 1);

            AssertKeysStoreDifferentResults (memo, key1, key2);
        }

        [Test]
        public void ShouldHaveDifferentEntriesForDifferentIndexes () {
            var memo = new ProductionMemoTable ();

            var sameProduction = new Mock<IProduction> ().Object;

            IProductionMemoKey key1 = memo.CreateKey (sameProduction, 1);
            IProductionMemoKey key2 = memo.CreateKey (sameProduction, 2);

            AssertKeysStoreDifferentResults (memo, key1, key2);
        }

        [Test]
        public void ClearResultShouldRemoveResult () {
            var memo = new ProductionMemoTable ();

            var context = new ParseContext ();
            var key = memo.CreateKey (new Mock<IProduction> ().Object, 3);
            var result = new ParseResult (5, null, context);
            memo.SetResult (key, result);

            Assert.That (memo.GetResult (key), Is.SameAs (result));

            memo.ClearResult (key);

            ParseResult noResult;
            Assert.False(memo.TryGetResult (key, out noResult));
        }

        [Test]
        public void ContainsKeyShouldReturnTrueIfEntryExists () {
            var memo = new ProductionMemoTable ();
            var key = memo.CreateKey (new Mock<IProduction> ().Object, 3);
            memo.SetResult (key, new ParseResult (0, null, null));

            Assert.True (memo.ContainsKey (key));
        }

        [Test]
        public void ContainsKeyShouldReturnFakseIfEntryDoesNotExist () {
            var memo = new ProductionMemoTable ();
            var key = memo.CreateKey (new Mock<IProduction> ().Object, 3);

            Assert.False (memo.ContainsKey (key));
        }

        private static void AssertKeysStoreDifferentResults (IProductionMemoTable memo, IProductionMemoKey key1, IProductionMemoKey key2) {
            var result1 = new ParseResult (0, null, null);
            memo.SetResult (key1, result1);
            var result2 = new ParseResult (1, null, null);
            memo.SetResult(key2, result2);

            Assert.That (memo.GetResult (key1), Is.Not.SameAs (result2));
            Assert.That (memo.GetResult(key2), Is.Not.SameAs (result1));

            Assert.That (memo.GetResult(key1), Is.SameAs (result1));
            Assert.That (memo.GetResult(key2), Is.SameAs (result2));
        }
    }
}